#ifndef _TOKENIZER_HPP_
#define _TOKENIZER_HPP_

#include "stdafx.h"
#include <iostream>
#include <string>
#include <vector>
#include <algorithm>
#include <utility>

#include <cassert>

class tokenizer {
public:
	typedef tokenizer _Self;
	typedef std::string value_type;
	typedef value_type* pointer;
	typedef const value_type* const_pointer;
	typedef std::vector<value_type> list_type;
	typedef list_type::iterator iterator;
	typedef list_type::const_iterator const_iterator;
public:
	typedef value_type& reference;
	typedef const value_type& const_reference;
	typedef list_type::size_type size_type;
	typedef value_type::value_type _CharT;
	typedef int flags_type;
	typedef std::pair<size_type, bool> findresult_type;


	enum {	// flags
		// mode flags
		skipmultidelim = 0x0001,
		csv            = 0x0002
	};

public:						// Constructor.
	tokenizer(_CharT __delim = ',', flags_type __modeflags = 0)
		: modeflags_(__modeflags), index_(0)  { delims_ = __delim; }
	tokenizer(const char* __delims, flags_type __modeflags = 0)
		: modeflags_(__modeflags), delims_(__delims), index_(0) {}
	tokenizer(const value_type& __delims, flags_type __modeflags = 0)
		: modeflags_(__modeflags), delims_(__delims), index_(0) {}
	tokenizer(const value_type& __line, _CharT __delim = ',',
		flags_type __modeflags = 0)
		: modeflags_(__modeflags) { delims_ = __delim; assign(__line); }
	tokenizer(const value_type& __line, const value_type& __delims, flags_type __modeflags = 0)
		: modeflags_(__modeflags), delims_(__delims) { assign(__line); }

public:						// Copy constructor.
	tokenizer(const _Self& __s) : modeflags_(__s.modeflags_), list_(__s.list_),
		delims_(__s.delims_), index_(__s.index_) {}

public:						// Override operator=.
	_Self& operator=(const value_type __rhs)
	{ return assign(__rhs); }
	_Self& operator=(const char* __rhs)
	{ return assign(value_type(__rhs)); }

	_Self& assign(const_reference __line) {
		value_type __tmp;
		size_type __idx_beg, __idx_end, __idx, __doublequote;
		clear();
		for (__idx_beg = 0; true; __idx_beg = __idx_end + 1)
		{
			__idx_end = __line.find_first_of(delims_, __idx_beg);
			__tmp.assign(__line, __idx_beg, __idx_end - __idx_beg);

			if (_M_get_mode_flags(csv))
			{
				if (__tmp[0] == '"')
				{
					while (true)
					{
						for (__idx = 1, __doublequote = 1; true;
							++__doublequote, ++__idx)
						{
							__idx = __tmp.find_first_of('"', __idx);
							if (__idx == value_type::npos) break;
						}

						if (__doublequote & 0x01)
						{
							if (__idx_end == value_type::npos) break;
							__idx_end = __line.find("\",", __idx_end + 1);
							if (__idx_end == value_type::npos) break;
							++__idx_end;
							__tmp.assign(__line, __idx_beg, __idx_end - __idx_beg);
							continue;
						}
						break;
					}

					// erase head and tail '"' char
					__tmp.erase(__tmp.size() - 1, 1).erase(0, 1);
					while(true) {
						__idx = __tmp.find("\"\"", __idx);
						if (__idx == value_type::npos) break;
						__tmp.erase(__idx, 1);
						++__idx;
					}
				}
			}

			_M_insert_to_list(__tmp);

			if (__idx_end == value_type::npos) break;
		}
		return *this;
	}

public:						// find string in tokenizer
	findresult_type find(const value_type& __string)
	{
		iterator __pos = std::find(list_.begin(), list_.end(), __string);
		if (__pos == list_.end()) return std::make_pair(0, false);

		return std::make_pair(std::distance(list_.begin(), __pos), true);
	}

public:						// Override operator++.
	_Self& operator++(void) { ++index_; return *this; }
	_Self operator++(int) { _Self __tmp(*this); ++index_; return __tmp; }

public:						// Override operator--.
	_Self& operator--(void) { --index_; return *this; }
	_Self operator--(int) { _Self __tmp(*this); --index_; return __tmp; }

public:						// Override operator*.
	reference operator*(void)
	{ assert(index_ < size()); return list_[index_]; }
	const_reference operator*(void) const
	{ assert(index_ < size()); return list_[index_]; }

public:						// Override operator[].
	reference operator[] (size_type __index)
	{ assert(__index < size()); return list_[__index]; }
	const_reference operator[] (size_type __index) const
	{ assert(__index < size()); return list_[__index]; }

public: 					// Delim control functions.
	value_type getDelim(void) { return delims_; }
	value_type setDelim(_CharT __delim)
	{ value_type __tmp(delims_); delims_ = __delim; return __tmp; }
	value_type setDelim(const value_type& __delims)
	{ value_type __tmp(delims_); delims_ = __delims; return __tmp; }

public:						// Flag-manipulation functions.
	flags_type getModeFlags(void) const {
		return _M_get_mode_flags();
	}
	flags_type setModeFlags(flags_type __flags) {
		flags_type __tmp = _M_get_mode_flags();
		_M_set_mode_flags(__flags);
		return __tmp;
	}
	flags_type setModeFlags(flags_type __flags, flags_type __mask) {
		flags_type __tmp = _M_get_mode_flags();
		_M_unset_mode_flags(__flags);
		_M_set_mode_flags(__flags & __mask);
		return __tmp;
	}

	void unsetModeFlags(flags_type __mask) {
		_M_unset_mode_flags(__mask);
	}

	inline bool isModeFlags(flags_type __flags) const {
		return _M_get_mode_flags() & __flags;
	}

public:						// tokenizer Mode manipulator.
	friend inline _Self& skipmultidelim(_Self& __s)
	{ __s.setModeFlags(skipmultidelim); return __s; }
	friend inline _Self& noskipmultidelim(_Self& __s)
	{ __s.unsetModeFlags(skipmultidelim); return __s; }
	friend inline _Self& csv(_Self& __s)
	{ __s.setDelim(','); __s.setModeFlags(csv); return __s; }
	friend inline _Self& nocsv(_Self& __s)
	{ __s.unsetModeFlags(csv); return __s; }

public:						// Override operator<<.
	typedef _Self& (*_ManipType) (_Self&);
	friend inline _Self& operator<<(_Self& __s, _ManipType __tokenizermanip)
	{ return __tokenizermanip(__s); }

public:
	size_type size(void) const { return list_.size(); }
	void clear(void) { index_ = 0; list_.clear(); }

public:						// Implicit conversion to bool.
	operator bool(void) const { return index_ < size(); }

private:
	void _M_insert_to_list(const value_type& __value) {
		if (_M_get_mode_flags(skipmultidelim)) {
			if (!__value.size()) return;
		}
		list_.push_back(__value);
	}

	flags_type _M_get_mode_flags(void) const {
		return modeflags_;
	}
	bool _M_get_mode_flags(flags_type __flags) const {
		return modeflags_ & __flags;
	}
	void _M_set_mode_flags(flags_type __flags) {
		modeflags_ |= __flags;
	}
	void _M_unset_mode_flags(flags_type __flags) {
		modeflags_ &= ~__flags;
	}

private:
	flags_type modeflags_;
	list_type list_;
	value_type delims_;
	size_type index_;
};

#endif // _TOKENIZE_HPP_
